"""
认证相关验证器
提供用户注册、登录时的数据验证功能
"""
import re
import time
from datetime import datetime, timedelta
from flask import request, current_app
from app import db

class AuthValidator:
    """认证数据验证器"""
    
    # 手机号格式验证（中国大陆手机号）
    PHONE_PATTERN = re.compile(r'^1[3-9]\d{9}$')
    
    # 微信code格式验证
    WECHAT_CODE_PATTERN = re.compile(r'^[a-zA-Z0-9_-]{20,40}$')
    
    # 注册频率限制配置
    REGISTER_LIMIT_PER_IP_HOUR = 10  # 每IP每小时最多注册10次
    REGISTER_LIMIT_PER_PHONE_DAY = 3  # 每手机号每天最多注册3次
    
    @classmethod
    def validate_phone_number(cls, phone):
        """验证手机号格式"""
        if not phone:
            raise ValueError("手机号不能为空")
        
        if not isinstance(phone, str):
            raise ValueError("手机号必须是字符串")
        
        if not cls.PHONE_PATTERN.match(phone):
            raise ValueError("手机号格式错误，请输入有效的中国大陆手机号")
        
        return True
    
    @classmethod
    def validate_wechat_code(cls, code):
        """验证微信登录code"""
        if not code:
            raise ValueError("微信登录code不能为空")
        
        if not isinstance(code, str):
            raise ValueError("微信登录code必须是字符串")
        
        if not cls.WECHAT_CODE_PATTERN.match(code):
            raise ValueError("微信登录code格式错误")
        
        return True
    
    @classmethod
    def validate_encrypted_data(cls, encrypted_data, iv):
        """验证加密数据格式"""
        if not encrypted_data:
            raise ValueError("加密数据不能为空")
        
        if not iv:
            raise ValueError("初始化向量不能为空")
        
        if not isinstance(encrypted_data, str) or not isinstance(iv, str):
            raise ValueError("加密数据和初始化向量必须是字符串")
        
        # 简单的长度检查
        if len(encrypted_data) < 20 or len(iv) < 20:
            raise ValueError("加密数据格式错误")
        
        return True
    
    @classmethod
    def check_register_frequency(cls, ip_address=None, phone=None):
        """检查注册频率限制"""
        if not ip_address:
            ip_address = cls._get_client_ip()
        
        # 检查IP注册频率
        if ip_address:
            cls._check_ip_register_frequency(ip_address)
        
        # 检查手机号注册频率
        if phone:
            cls._check_phone_register_frequency(phone)
    
    @classmethod
    def _get_client_ip(cls):
        """获取客户端IP地址"""
        # 考虑代理服务器的情况
        if request.headers.get('X-Forwarded-For'):
            return request.headers.get('X-Forwarded-For').split(',')[0].strip()
        elif request.headers.get('X-Real-IP'):
            return request.headers.get('X-Real-IP')
        else:
            return request.remote_addr
    
    @classmethod
    def _check_ip_register_frequency(cls, ip_address):
        """检查IP注册频率"""
        # 这里应该使用Redis或数据库来记录IP注册次数
        # 为了简化，先用内存缓存（生产环境建议使用Redis）
        
        cache_key = f"register_ip_{ip_address}"
        current_hour = datetime.now().strftime('%Y%m%d%H')
        cache_key_with_hour = f"{cache_key}_{current_hour}"
        
        # 简化实现：使用应用内存（仅限演示）
        if not hasattr(current_app, '_register_cache'):
            current_app._register_cache = {}
        
        register_count = current_app._register_cache.get(cache_key_with_hour, 0)
        
        if register_count >= cls.REGISTER_LIMIT_PER_IP_HOUR:
            raise ValueError(f"注册过于频繁，每小时最多注册{cls.REGISTER_LIMIT_PER_IP_HOUR}次，请稍后再试")
        
        # 增加计数
        current_app._register_cache[cache_key_with_hour] = register_count + 1
        
        # 清理过期的缓存（保留最近2小时的数据）
        current_time = datetime.now()
        for key in list(current_app._register_cache.keys()):
            if key.startswith('register_ip_'):
                try:
                    hour_str = key.split('_')[-1]
                    key_time = datetime.strptime(hour_str, '%Y%m%d%H')
                    if current_time - key_time > timedelta(hours=2):
                        del current_app._register_cache[key]
                except:
                    pass
    
    @classmethod
    def _check_phone_register_frequency(cls, phone):
        """检查手机号注册频率"""
        # 查询数据库中该手机号今天的注册次数
        today = datetime.now().date()
        
        # 这里需要一个用户注册日志表来记录
        # 为了简化，先检查用户表中的创建时间
        from app.models.user import User
        
        today_registers = User.query.filter(
            User.phone == phone,
            db.func.date(User.created_at) == today
        ).count()
        
        if today_registers >= cls.REGISTER_LIMIT_PER_PHONE_DAY:
            raise ValueError(f"该手机号今日注册次数过多，每天最多注册{cls.REGISTER_LIMIT_PER_PHONE_DAY}次")
    
    @classmethod
    def validate_user_info(cls, user_info):
        """验证用户信息"""
        if not user_info or not isinstance(user_info, dict):
            return True  # 用户信息是可选的
        
        # 验证昵称
        nickname = user_info.get('nickName')
        if nickname:
            if not isinstance(nickname, str):
                raise ValueError("昵称必须是字符串")
            
            if len(nickname.strip()) == 0:
                raise ValueError("昵称不能为空")
            
            if len(nickname) > 20:
                raise ValueError("昵称长度不能超过20个字符")
            
            # 检查是否包含敏感词（简化实现）
            sensitive_words = ['admin', '管理员', '客服', '官方']
            nickname_lower = nickname.lower()
            for word in sensitive_words:
                if word in nickname_lower:
                    raise ValueError(f"昵称不能包含敏感词：{word}")
        
        # 验证性别
        gender = user_info.get('gender')
        if gender is not None:
            if gender not in [0, 1, 2]:  # 0=未知, 1=男, 2=女
                raise ValueError("性别参数错误")
        
        return True
    
    @classmethod
    def validate_openid(cls, openid):
        """验证微信openid格式"""
        if not openid:
            raise ValueError("openid不能为空")
        
        if not isinstance(openid, str):
            raise ValueError("openid必须是字符串")
        
        # 微信openid通常是28位长度的字符串
        if len(openid) != 28:
            raise ValueError("openid格式错误")
        
        # 只包含字母、数字、下划线、连字符
        if not re.match(r'^[a-zA-Z0-9_-]+$', openid):
            raise ValueError("openid包含非法字符")
        
        return True
    
    @classmethod
    def clean_user_input(cls, data):
        """清理用户输入数据"""
        if not isinstance(data, dict):
            return data
        
        cleaned_data = {}
        for key, value in data.items():
            if isinstance(value, str):
                # 移除前后空格
                value = value.strip()
                # 移除特殊字符（基本的XSS防护）
                value = cls._sanitize_string(value)
            cleaned_data[key] = value
        
        return cleaned_data
    
    @classmethod
    def _sanitize_string(cls, text):
        """清理字符串，防止XSS攻击"""
        if not text:
            return text
        
        # 移除HTML标签
        import html
        text = html.escape(text)
        
        # 移除可能的脚本内容
        dangerous_patterns = [
            r'<script.*?</script>',
            r'javascript:',
            r'vbscript:',
            r'onload=',
            r'onclick=',
            r'onerror='
        ]
        
        for pattern in dangerous_patterns:
            text = re.sub(pattern, '', text, flags=re.IGNORECASE)
        
        return text 